www.gusucode.com > VC++ 多声道MP3录音实时压缩保存程序-源码程序 > VC++ 多声道MP3录音实时压缩保存程序-源码程序/code/AudioPlayRec.cpp
// AudioPlayRec.cpp : implementation file // Download by http://www.NewXing.com #include "stdafx.h" #include "hwaudiorec.h" #include "AudioPlayRec.h" #include <math.h> #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif BOOL CALLBACK InputLineProc ( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue ) { CAudioPlayRec *pAudioPlayRec = reinterpret_cast<CAudioPlayRec*>(dwUserValue); ASSERT ( pAudioPlayRec ); CString csShortName = pLineInfo->szShortName; csShortName.MakeLower (); if ( csShortName.Find ( "microphone" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ) { pAudioPlayRec->m_uLineIndex_Microphone = pLineInfo->dwSource; } else if ( csShortName.Find ( "line in" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE ) { pAudioPlayRec->m_uLineIndex_LineIn = pLineInfo->dwSource; } TRACE ( "csShortName = %s, dwComponentType = %d\n", csShortName, pLineInfo->dwComponentType ); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CAudioPlayRec CAudioPlayRec::CAudioPlayRec () : m_eStatus ( ENUM_STATUS_INVALID ) , m_hRecord ( NULL ) , m_hPlay ( NULL ) , m_dwQueuBufferSize ( 1024 ) , m_nDataQueueNum ( 0 ) , m_bRecording ( FALSE ) , m_eRecChannel ( ENUM_REC_CHANNEL_MONO ) , m_szAryInData ( NULL ) , m_szLeftInData ( NULL ) , m_szRightInData ( NULL ) , m_pAryHdr ( NULL ) , m_bAlwaysDrawTowChannel ( FALSE ) , m_uLineIndex_Microphone ( 0 ) , m_uLineIndex_LineIn ( 0 ) { memset ( &m_Format, 0, sizeof(WAVEFORMATEX) ); ZeroMemory(&m_MMCKInfoParent,sizeof(m_MMCKInfoParent)); ZeroMemory(&m_MMCKInfoChild,sizeof(m_MMCKInfoChild)); memset ( m_hWaveFile, 0, sizeof(m_hWaveFile) ); ResetMp3EncodeVar (); m_clrBK = RGB ( 0,0,0 ); SetBkColor ( m_clrBK ); } CAudioPlayRec::~CAudioPlayRec() { StopAndFreeAll (); if ( m_brsBkGnd.GetSafeHandle() ) { m_brsBkGnd.DeleteTempMap(); m_brsBkGnd.DeleteObject(); } if ( m_PenB.GetSafeHandle() ) m_PenB.DeleteObject(); if ( m_PenG.GetSafeHandle() ) m_PenG.DeleteObject(); if ( m_PenPartLine.GetSafeHandle() ) m_PenPartLine.DeleteObject(); if ( m_fntChannelText.GetSafeHandle() ) m_fntChannelText.DeleteObject(); if ( m_fntDeviceNameText.GetSafeHandle() ) m_fntDeviceNameText.DeleteObject(); } BEGIN_MESSAGE_MAP(CAudioPlayRec, CWnd) //{{AFX_MSG_MAP(CAudioPlayRec) ON_WM_ERASEBKGND() ON_WM_SETCURSOR() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CAudioPlayRec message handlers void CAudioPlayRec::SetWaveFormat ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample ) { m_eRecChannel = eRecChannel; memset ( &m_Format, 0, sizeof(WAVEFORMATEX) ); m_Format.cbSize = 0; m_Format.wFormatTag = WAVE_FORMAT_PCM; m_Format.wBitsPerSample = wBitsPerSample; m_Format.nChannels = ( (eRecChannel==ENUM_REC_CHANNEL_MONO) ? 1 : 2 ); m_Format.nSamplesPerSec = nSamplesPerSec; m_Format.nAvgBytesPerSec = m_Format.nSamplesPerSec * (m_Format.wBitsPerSample/8); m_Format.nBlockAlign = m_Format.nChannels * (m_Format.wBitsPerSample/8); } BOOL CAudioPlayRec::SetRelateParaAfterGetWaveFormat () { FreeBuffer (); m_wInQueu = (WORD) ( m_Format.nChannels + m_Format.wBitsPerSample/8 + m_Format.nSamplesPerSec/11025 ); if ( !AllocateBuffer ( m_dwQueuBufferSize ) ) return FALSE; return TRUE; } BOOL CAudioPlayRec::Create ( HWND hwndParent, LPRECT lpRect/*=NULL*/ ) { LPCTSTR lpszClassName = AfxRegisterWndClass( 0, LoadCursor(AfxGetInstanceHandle(), IDC_ARROW), NULL, NULL ); CRect rc ( 0,0,0,0 ); if ( lpRect ) rc = *lpRect; if ( !CreateEx ( 0, lpszClassName, "", WS_CHILD | WS_TABSTOP, rc.left, rc.top, rc.Width(), rc.Height(), hwndParent, NULL, NULL) ) { AfxMessageBox ( "Create window failed" ); return FALSE; } if ( lpRect ) ShowWindow ( SW_SHOW ); else ShowWindow ( SW_HIDE ); GetClientRect ( &m_rcClient ); CClientDC dc(this); m_fntChannelText.CreatePointFont ( 100, "Impact", &dc ); m_fntDeviceNameText.CreateFont ( 14, 0, 0, 0, 0, TRUE, TRUE, FALSE, 0, 0, 0, 0, 0, "MS Sans Serif" ); return TRUE; } BOOL CAudioPlayRec::OnEraseBkgnd(CDC* pDC) { DrawBackground ( pDC ); return CWnd::OnEraseBkgnd(pDC); } void CAudioPlayRec::DrawBackground ( CDC *pDC ) { pDC->SetBkMode ( TRANSPARENT ); DrawBackground ( pDC, TRUE ); DrawBackground ( pDC, FALSE ); // 画分隔线 CPen *pOldPen = NULL; if ( m_PenPartLine.GetSafeHandle() ) pOldPen = pDC->SelectObject ( &m_PenPartLine ); pDC->MoveTo ( 0, m_rcClient.Height()/2 ); pDC->LineTo ( m_rcClient.right, m_rcClient.Height()/2 ); if ( pOldPen ) pDC->SelectObject ( pOldPen ); // 画声卡名文字 CFont *pOldFnt = NULL; CRect rcText = m_rcClient; rcText.DeflateRect ( 4, 4 ); if ( m_fntDeviceNameText.GetSafeHandle() ) { pOldFnt = pDC->SelectObject ( &m_fntDeviceNameText ); } pDC->SetTextColor ( RGB(128,255,255) ); pDC->DrawText ( m_csDeviceName, &rcText, DT_RIGHT | DT_BOTTOM | DT_SINGLELINE ); if ( pOldFnt ) pDC->SelectObject ( pOldFnt ); // 画外框 pDC->Draw3dRect ( &m_rcClient, COLOR_FRAME, COLOR_FRAME ); } CRect CAudioPlayRec::GetRectByChannel ( BOOL bLeftChannel ) { CRect rcBK = m_rcClient; if ( bLeftChannel ) { rcBK.bottom = (m_rcClient.Height() - PARTLINE_HEIGHT) / 2; } else { rcBK.top = (m_rcClient.Height() + PARTLINE_HEIGHT) / 2; } return rcBK; } void CAudioPlayRec::DrawBackground ( CDC *pDC, BOOL bLeftChannel ) { ASSERT ( pDC ); if ( !m_brsBkGnd.GetSafeHandle() ) return; // 画背景 CString csCaption; CRect rcBK = GetRectByChannel ( bLeftChannel ); if ( bLeftChannel ) { csCaption = "Left"; } else { csCaption = "Right"; } pDC->FillRect ( &rcBK, &m_brsBkGnd ); // 画文字 CFont *pOldFnt = NULL; if ( m_fntChannelText.GetSafeHandle() ) { pOldFnt = pDC->SelectObject ( &m_fntChannelText ); } CRect rcText = rcBK; rcText.DeflateRect ( 2, 2 ); pDC->SetTextColor ( RGB(255,255,0) ); pDC->DrawText ( csCaption, &rcText, DT_LEFT | DT_TOP | DT_SINGLELINE ); if ( pOldFnt ) pDC->SelectObject ( pOldFnt ); } void CAudioPlayRec::DrawWave ( DWORD dwChannelBytes ) { CClientDC dc(this); BOOL bRecLeft = TRUE, bRecRight = TRUE; if ( !m_bAlwaysDrawTowChannel && m_eStatus == ENUM_STATUS_RECORDING && m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) { if ( !m_hWaveFile[ENUM_FILE_CHANNEL_LEFT] && !m_pFileMp3[ENUM_FILE_CHANNEL_LEFT] ) bRecLeft = FALSE; if ( !m_hWaveFile[ENUM_FILE_CHANNEL_RIGHT] && !m_pFileMp3[ENUM_FILE_CHANNEL_RIGHT] ) bRecRight = FALSE; } // 先将上次画的擦掉 DrawBackground ( &dc ); // 画波形图 CPen *pOndPen = dc.SelectObject ( &m_PenG ); if ( m_Format.wBitsPerSample == 8 ) { if ( bRecLeft ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szLeftInData, TRUE ); if ( bRecRight ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szRightInData, FALSE ); } else { if ( bRecLeft ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szLeftInData, TRUE ); if ( bRecRight ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szRightInData, FALSE ); } if ( pOndPen ) dc.SelectObject ( pOndPen ); } void CAudioPlayRec::DrwaWaveShort ( CClientDC &dc, DWORD dwDrawBytes, SHORT *pShortData, BOOL bLeftChannel ) { CRect rcBK = GetRectByChannel ( bLeftChannel ); int nCenterY = rcBK.CenterPoint().y; int y = nCenterY + (int) ( pShortData[0] * rcBK.Height() / 0xffff ); dc.MoveTo ( 0, y ); float fStep = (float)rcBK.Width() / (float)(dwDrawBytes); float fLineX = 0; for ( DWORD i=1; i<dwDrawBytes; i++ ) { fLineX += fStep; y = nCenterY + (int) ( pShortData[i] * rcBK.Height() / 0xffff ); dc.LineTo ( (int)fLineX, y ); } } void CAudioPlayRec::DrwaWaveChar ( CClientDC &dc, DWORD dwDrawBytes, BYTE *pCharData, BOOL bLeftChannel ) { CRect rcBK = GetRectByChannel ( bLeftChannel ); int y = (int) ( pCharData[0] * rcBK.Height() / 0xff ); dc.MoveTo ( 0, y ); float fStep = (float)rcBK.Width() / (float)(dwDrawBytes); float fLineX = 0; for ( DWORD i=1; i<dwDrawBytes; i++ ) { fLineX += fStep; y = (int) ( pCharData[i] * rcBK.Height() / 0xff ); dc.LineTo ( (int)fLineX, y ); } } BOOL CAudioPlayRec::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { SetCursor ( ::LoadCursor ( NULL, IDC_ARROW ) ); return CWnd::OnSetCursor(pWnd, nHitTest, message); } BOOL CAudioPlayRec::Init ( UINT uDeviceID/*=WAVE_MAPPER*/, DWORD dwBufferSize/*=1024*/, HWND hwndParent/*=NULL*/, LPRECT lpRect/*=NULL*/, FUNC_CallbackNotify Proc_CallbackNotify/*=NULL*/, WPARAM wParam/*=NULL*/ ) { SetDeviceID ( uDeviceID ); if ( m_uDeviceID != WAVE_MAPPER && (m_uDeviceID > GetWaveInCount() || m_uDeviceID > GetWaveOutCount()) ) { AfxMessageBox ( "Error audio device" ); return FALSE; } m_dwQueuBufferSize = dwBufferSize; m_Proc_CallbackNotify = Proc_CallbackNotify; m_wParam = wParam; // 创建窗口用来接收消息 if ( !Create ( hwndParent, lpRect ) ) return FALSE; if ( hwndParent && lpRect ) { m_PenG.CreatePen ( PS_SOLID, 0, RGB(0, 255, 0) ); m_PenPartLine.CreatePen ( PS_SOLID, PARTLINE_HEIGHT, COLOR_FRAME ); } m_eStatus = ENUM_STATUS_READY; return TRUE; } void CAudioPlayRec::SetDeviceID ( UINT uDeviceID ) { StopAndFreeAll (); m_uDeviceID = uDeviceID; m_csDeviceName = GetWaveInName ( m_uDeviceID ); // 找录音线路的序号 CVolumeInXXX::EnumerateInputLines ( m_uDeviceID, InputLineProc, (DWORD)this ); if ( ::IsWindow ( m_hWnd ) ) { CRect rcBK = GetRectByChannel ( FALSE ); rcBK.DeflateRect ( 4, 4 ); InvalidateRect ( &rcBK ); } } BOOL CAudioPlayRec::Record ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample ) { // 录音设备正在录音 if ( m_eStatus == ENUM_STATUS_RECORDING ) { TRACE ( "录音设备正在录音 ...\n" ); return TRUE; } m_bRecording = TRUE; MMRESULT mmReturn = 0; ResetMp3EncodeVar (); ASSERT ( (wBitsPerSample%8) == 0 ); if ( wBitsPerSample > 16 ) wBitsPerSample = 16; if ( m_eStatus != ENUM_STATUS_READY ) { AfxMessageBox ( "Class status error" ); return FALSE; } SetWaveFormat ( eRecChannel, nSamplesPerSec, wBitsPerSample ); if ( !SetRelateParaAfterGetWaveFormat () ) { return FALSE; } // open wavein device mmReturn = ::waveInOpen ( &m_hRecord, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInOpen()"); goto failed; } else { // make several input buffers and add them to the input queue for(int i=0; i<m_wInQueu; i++) { AddInputBufferToQueue ( i ); } // start recording mmReturn = ::waveInStart ( m_hRecord ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInStart() failed"); goto failed; } } m_eStatus = ENUM_STATUS_RECORDING; return TRUE; failed: FreeBuffer (); return FALSE; } BOOL CAudioPlayRec::StartRecordAudioFile ( ENUM_FILE_CHANNEL eFileChannel, LPCTSTR lpszAudioFileName ) { ASSERT ( ( ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) && ( eFileChannel == ENUM_FILE_CHANNEL_LEFT || eFileChannel == ENUM_FILE_CHANNEL_RIGHT ) ) || ( ( m_eRecChannel != ENUM_REC_CHANNEL_ALONE ) && ( eFileChannel == ENUM_FILE_CHANNEL_COMMON ) ) ); ASSERT ( lpszAudioFileName && strlen(lpszAudioFileName) > 0 ); CString csFileName = lpszAudioFileName; csFileName.MakeLower (); // 需要保存到mp3文件中 if ( csFileName.Find ( ".mp3" ) == csFileName.GetLength() - 4 ) { if ( m_Format.wBitsPerSample == 8 ) { AfxMessageBox ( "Cannot record 8 bits mp3" ); goto failed; } if ( !LoadMp3DllFunc () ) { goto failed; } if ( !PrepareEncodeMp3 ( csFileName, eFileChannel ) ) { goto failed; } } else { // 创建一个wave文件 if ( !CreateWaveFile ( csFileName, eFileChannel ) ) { goto failed; } } return TRUE; failed: FreeBuffer (); return FALSE; } void CAudioPlayRec::StopRecordAudioFile ( ENUM_FILE_CHANNEL eFileChannel, CString csStopFileType/*="mp3"*/ ) { csStopFileType.MakeLower (); if ( csStopFileType=="wav" && m_hWaveFile[eFileChannel] ) { ::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 ); ::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoParent[eFileChannel], 0 ); ::mmioClose ( m_hWaveFile[eFileChannel], 0 ); m_hWaveFile[eFileChannel] = NULL; } if ( csStopFileType=="mp3" && m_ForMp3_hDLL_LameEnc ) { EndEncodeMp3 ( eFileChannel ); } } BOOL CAudioPlayRec::AddInputBufferToQueue ( int nIndex ) { ASSERT ( nIndex >= 0 && nIndex < m_wInQueu ); ASSERT ( m_szAryInData[nIndex] ); MMRESULT mmReturn = 0; LPWAVEHDR pHdr = m_pAryHdr[nIndex]; ZeroMemory ( pHdr, sizeof(WAVEHDR) ); pHdr->lpData = (char*)m_szAryInData[nIndex]; pHdr->dwBufferLength = m_dwQueuBufferSize; // prepare it mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "AddInputBufferToQueue Failed"); return FALSE; } // add the input buffer to the queue mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInAddBuffer() failed"); return FALSE; } m_nDataQueueNum ++; // no error return TRUE; } void CAudioPlayRec::waveErrorMsg ( MMRESULT result, LPCTSTR addstr ) { // say error message char errorbuffer[100]; if ( m_bRecording ) waveInGetErrorText ( result, errorbuffer, 100 ); else waveOutGetErrorText ( result, errorbuffer, 100 ); CString csMsg; csMsg.Format ( "WAVEIN:%x:%s %s", result, errorbuffer, addstr ); AfxMessageBox ( csMsg ); } LRESULT CAudioPlayRec::OnMM_WIM_DATA ( WPARAM wParam, LPARAM lParam ) { MMRESULT mmReturn = 0; LPWAVEHDR pHdr = (LPWAVEHDR) lParam; ASSERT ( pHdr ); mmReturn = ::waveInUnprepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR)); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInUnprepareHeader() failed" ); return -1L; } if( m_eStatus == ENUM_STATUS_RECORDING ) { // 提取单声道PCM数据 int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded ); // 根据需要保存的通道文件类型选择PCM数据和数据长度 char *pRecData[ENUM_FILE_CHANNEL_NUM] = { pHdr->lpData, pHdr->lpData }; int nRecBytes[ENUM_FILE_CHANNEL_NUM] = { pHdr->dwBytesRecorded, pHdr->dwBytesRecorded }; if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) { pRecData[ENUM_FILE_CHANNEL_LEFT] = m_szLeftInData; nRecBytes[ENUM_FILE_CHANNEL_LEFT] = nBytesPickup; pRecData[ENUM_FILE_CHANNEL_RIGHT] = m_szRightInData; nRecBytes[ENUM_FILE_CHANNEL_RIGHT] = nBytesPickup; } // 保存到wave文件中 for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ ) { if ( m_hWaveFile[eFileChannel] ) { int length = ::mmioWrite ( m_hWaveFile[eFileChannel], pRecData[eFileChannel], nRecBytes[eFileChannel] ); if ( length != nRecBytes[eFileChannel] ) { Stop (); m_nDataQueueNum --; AfxMessageBox ( "Write file failed" ); return -1L; } } } // 保存到mp3文件中 if ( m_ForMp3_hDLL_LameEnc ) { DWORD dwSamplesBytes = m_dwSamplesEncodeMp3Block * (m_Format.wBitsPerSample/8); for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ ) { if ( m_pFileMp3[eFileChannel] ) { int nRemainSize = m_ForMp3_dwWaveBufferSize - m_ForMp3_dwWaveDataBytes[eFileChannel]; int nCopyBytes = (nRemainSize < nRecBytes[eFileChannel]) ? nRemainSize : nRecBytes[eFileChannel]; memcpy ( m_ForMp3_pWaveBuffer[eFileChannel]+m_ForMp3_dwWaveDataBytes[eFileChannel], pRecData[eFileChannel], nCopyBytes ); m_ForMp3_dwWaveDataBytes[eFileChannel] += nCopyBytes; // mp3压缩 for ( ; m_ForMp3_dwWaveDataBytes[eFileChannel] > dwSamplesBytes; m_ForMp3_dwWaveDataBytes[eFileChannel] -= dwSamplesBytes ) { WaveBufferMp3Encode ( (char*)m_ForMp3_pWaveBuffer[eFileChannel], (int)dwSamplesBytes, (ENUM_FILE_CHANNEL)eFileChannel ); memmove ( m_ForMp3_pWaveBuffer[eFileChannel], m_ForMp3_pWaveBuffer[eFileChannel]+dwSamplesBytes, m_ForMp3_dwWaveDataBytes[eFileChannel]-dwSamplesBytes ); } char *pUnCopyData = pRecData[eFileChannel] + nCopyBytes; int nUnCopyDataBytes = nRecBytes[eFileChannel] - nCopyBytes; nRemainSize = m_ForMp3_dwWaveBufferSize - m_ForMp3_dwWaveDataBytes[eFileChannel]; nCopyBytes = (nRemainSize < nUnCopyDataBytes) ? nRemainSize : nUnCopyDataBytes; memcpy ( m_ForMp3_pWaveBuffer[eFileChannel]+m_ForMp3_dwWaveDataBytes[eFileChannel], pUnCopyData, nCopyBytes ); } } } // reuse the buffer: // prepare it again mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInPrepareHeader() failed" ); } else // no error { // add the input buffer to the queue again mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveInAddBuffer() failed"); } else { DrawWave( (DWORD)nBytesPickup ); return 0L; // no error } } } // 停止录音 else { if ( m_nDataQueueNum == 1 ) { StopRec (); } else { m_nDataQueueNum --; } } return 0L; } LRESULT CAudioPlayRec::OnMM_WOM_DONE(WPARAM wParam, LPARAM lParam) { MMRESULT mmReturn = 0; LPWAVEHDR pHdr = (LPWAVEHDR) lParam; mmReturn = ::waveOutUnprepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveOutUnprepareHeader() failed" ); return -1L; } m_nDataQueueNum--; if ( m_eStatus == ENUM_STATUS_PLAYING ) { int nSize = m_dwQueuBufferSize; if ( ReadSoundDataFromFile ( pHdr->lpData, nSize ) ) { AddOutputBufferToQueue ( (int)pHdr->dwUser, nSize ); return 0L; } else { Stop(); } } // we are closing the waveOut handle, // all data must be deleted // this buffer was allocated in Start() if ( m_nDataQueueNum == 0 && m_eStatus != ENUM_STATUS_PLAYING ) { StopPlay (); } return 0L; } // // 创建一个wave文件 // BOOL CAudioPlayRec::CreateWaveFile ( LPCTSTR lpszWaveFileName, ENUM_FILE_CHANNEL eFileChannel ) { ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); if ( m_hWaveFile[eFileChannel] ) return TRUE; ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 ); ::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_DELETE ); // check if file is already open if ( m_hWaveFile[eFileChannel] ) return TRUE; WAVEFORMATEX wfx = m_Format; if ( m_eRecChannel==ENUM_REC_CHANNEL_ALONE ) wfx.nChannels = 1; // open file m_hWaveFile[eFileChannel] = ::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_CREATE|MMIO_WRITE|MMIO_EXCLUSIVE|MMIO_ALLOCBUF ); if ( m_hWaveFile[eFileChannel] == NULL ) { AfxMessageBox ( "Open wave file failed" ); return FALSE; } ZeroMemory ( &m_MMCKInfoParent[eFileChannel], sizeof(MMCKINFO) ); m_MMCKInfoParent[eFileChannel].fccType = mmioFOURCC('W','A','V','E'); MMRESULT mmResult = ::mmioCreateChunk( m_hWaveFile[eFileChannel],&m_MMCKInfoParent[eFileChannel], MMIO_CREATERIFF); ZeroMemory ( &m_MMCKInfoChild[eFileChannel], sizeof(MMCKINFO) ); m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('f','m','t',' '); m_MMCKInfoChild[eFileChannel].cksize = sizeof(WAVEFORMATEX) + wfx.cbSize; mmResult = ::mmioCreateChunk(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0); mmResult = ::mmioWrite(m_hWaveFile[eFileChannel], (char*)&wfx, sizeof(WAVEFORMATEX) + wfx.cbSize); mmResult = ::mmioAscend(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0); m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('d', 'a', 't', 'a'); mmResult = ::mmioCreateChunk ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 ); return TRUE; } void CAudioPlayRec::Stop() { if ( m_eStatus != ENUM_STATUS_PLAYING && m_eStatus != ENUM_STATUS_RECORDING ) return; MMRESULT mmReturn = 0; if ( m_eStatus == ENUM_STATUS_PLAYING ) { if ( ::waveOutReset(m_hPlay) ) waveErrorMsg ( mmReturn, "waveOutReset() failed"); SetTimer ( TIMER_EVENT_STOPPLAY, 1000, NULL ); } else if ( m_eStatus == ENUM_STATUS_RECORDING ) { SetTimer ( TIMER_EVENT_STOPREC, 1000, NULL ); } Invalidate ( TRUE ); m_eStatus = ENUM_STATUS_STOPING; } // // 打开一个wave文件 // BOOL CAudioPlayRec::OpenWaveFile(LPCTSTR lpszWaveFileName) { ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 ); // check if file is already open if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ) return FALSE; m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = ::mmioOpen ( (LPTSTR)lpszWaveFileName,NULL,MMIO_READ ); if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] == NULL ) { AfxMessageBox ( "Open wave file failed" ); return FALSE; } m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON].fccType = mmioFOURCC('W','A','V','E'); MMRESULT mmResult = ::mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],NULL,MMIO_FINDRIFF); if(mmResult) { AfxMessageBox("Error descending into file"); ::mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; return FALSE; } m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('f','m','t',' '); mmResult = mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],MMIO_FINDCHUNK); if(mmResult) { AfxMessageBox("Error descending in wave file"); mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; return FALSE; } DWORD bytesRead = mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],(LPSTR)&m_Format, m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].cksize ); if ( bytesRead < 0 ) { AfxMessageBox ( "Error reading PCM wave format record" ); mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 ); return FALSE; } if ( !SetRelateParaAfterGetWaveFormat () ) return FALSE; // open output sound file mmResult = mmioAscend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], 0 ); if ( mmResult ) { AfxMessageBox ( "Error ascending in File" ); mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 ); m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; return FALSE; } m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('d','a','t','a'); mmResult = mmioDescend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON], MMIO_FINDCHUNK ); if ( mmResult ) { AfxMessageBox("error reading data chunk"); mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; return FALSE; } return TRUE; } BOOL CAudioPlayRec::Play(LPCTSTR lpszWaveFileName) { m_bRecording = FALSE; if ( m_eStatus != ENUM_STATUS_READY ) { AfxMessageBox ( "Class status error" ); return FALSE; } if ( !OpenWaveFile ( lpszWaveFileName ) ) return FALSE; MMRESULT mmReturn = 0; // open wavein device mmReturn = ::waveOutOpen ( &m_hPlay, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveOutOpen() failed"); return FALSE; } else { // make several input buffers and add them to the input queue for(int i=0; i<m_wInQueu; i++) { int nSize = m_dwQueuBufferSize; if ( !ReadSoundDataFromFile ( m_szAryInData[i], nSize ) ) { if ( i == 0 ) { AfxMessageBox ( "Read sound data from file failed" ); return FALSE; } } if ( !AddOutputBufferToQueue ( i, nSize ) ) return FALSE; } } m_eStatus = ENUM_STATUS_PLAYING; return TRUE; } BOOL CAudioPlayRec::ReadSoundDataFromFile ( LPVOID data, int &size ) { ASSERT ( data && size > 0 ); ASSERT ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ); return ( ( size = ::mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], (char*)data, size) ) > 0 ); } BOOL CAudioPlayRec::AddOutputBufferToQueue ( int nIndex, int nSize ) { ASSERT ( nIndex >= 0 && nIndex < m_wInQueu ); ASSERT ( m_szAryInData[nIndex] ); MMRESULT mmReturn = 0; // create the header LPWAVEHDR pHdr = m_pAryHdr[nIndex]; memset ( pHdr, 0, sizeof(WAVEHDR) ); // new a buffer pHdr->lpData = (char*)m_szAryInData[nIndex]; pHdr->dwBufferLength = m_dwQueuBufferSize; pHdr->dwBytesRecorded = nSize; pHdr->dwFlags = 0; pHdr->dwUser = nIndex; // prepare it mmReturn = ::waveOutPrepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) { waveErrorMsg ( mmReturn, "waveOutPrepareHeader() failed"); return FALSE; } // write the buffer to output queue mmReturn = ::waveOutWrite ( m_hPlay, pHdr, sizeof(WAVEHDR) ); if ( mmReturn ) waveErrorMsg ( mmReturn, "waveOutWrite() failed"); // increment the number of waiting buffers m_nDataQueueNum++; int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded ); DrawWave( (DWORD)nBytesPickup ); return TRUE; } void CAudioPlayRec::StopRec () { if ( !m_hRecord ) return; if ( m_eStatus != ENUM_STATUS_RECORDING && m_eStatus != ENUM_STATUS_STOPING ) return; MMRESULT mmReturn = 0; mmReturn = ::waveInReset ( m_hRecord ); if ( mmReturn ) waveErrorMsg ( mmReturn, "waveInReset() failed" ); ::Sleep ( 10 ); for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ ) { StopRecordAudioFile ( (ENUM_FILE_CHANNEL)eFileChannel, "mp3" ); StopRecordAudioFile ( (ENUM_FILE_CHANNEL)eFileChannel, "wav" ); } mmReturn = ::waveInClose ( m_hRecord ); m_hRecord = NULL; if ( mmReturn ) waveErrorMsg ( mmReturn, "waveInClose() failed" ); FreeMp3Encode (); } void CAudioPlayRec::StopPlay() { MMRESULT mmReturn = 0; if ( !m_hPlay ) return; if ( m_eStatus != ENUM_STATUS_PLAYING && m_eStatus != ENUM_STATUS_STOPING ) return; waveOutPause ( m_hPlay ); mmReturn = ::waveOutClose ( m_hPlay ); if ( mmReturn ) waveErrorMsg ( mmReturn, "waveOutClose() failed" ); m_hPlay = NULL; if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ) { ::mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 ); m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; } m_eStatus = ENUM_STATUS_READY; } // // 申请内存 // BOOL CAudioPlayRec::AllocateBuffer ( DWORD dwBufferSize ) { m_dwQueuBufferSize = dwBufferSize; ASSERT ( m_wInQueu > 0 ); m_szAryInData = new char*[m_wInQueu]; m_szLeftInData = new char[m_dwQueuBufferSize]; m_szRightInData = new char[m_dwQueuBufferSize]; m_pAryHdr = new WAVEHDR*[m_wInQueu]; if ( !m_szAryInData || !m_szLeftInData || !m_szRightInData || !m_pAryHdr ) { ::AfxThrowMemoryException (); return FALSE; } memset ( m_szAryInData, 0, sizeof(char*)*m_wInQueu ); memset ( m_szLeftInData, 0, sizeof(char)*m_dwQueuBufferSize ); memset ( m_szRightInData, 0, sizeof(char)*m_dwQueuBufferSize ); memset ( m_pAryHdr, 0, sizeof(WAVEHDR*)*m_wInQueu ); for ( int i=0; i<m_wInQueu; i++ ) { m_szAryInData[i] = new char[m_dwQueuBufferSize]; m_pAryHdr[i] = new WAVEHDR; if ( !m_szAryInData[i] || !m_pAryHdr[i] ) { ::AfxThrowMemoryException (); return FALSE; } memset ( m_szAryInData[i], 0, m_dwQueuBufferSize ); memset ( m_pAryHdr[i], 0, sizeof(WAVEHDR) ); } return TRUE; } void CAudioPlayRec::FreeBuffer () { // 录音或者播放尚未停止,不能释放内存 for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ ) { if ( m_hWaveFile[eFileChannel] || m_pFileMp3[eFileChannel] ) return; } if ( m_szAryInData ) { for ( int i=0; i<m_wInQueu; i++ ) { if ( m_szAryInData[i] ) delete[] m_szAryInData[i]; } memset ( m_szAryInData, 0, sizeof(char*)*m_wInQueu ); delete[] m_szAryInData; m_szAryInData = NULL; } if ( m_szLeftInData ) { delete[] m_szLeftInData; m_szLeftInData = NULL; } if ( m_szRightInData ) { delete[] m_szRightInData; m_szRightInData = NULL; } if ( m_pAryHdr ) { for ( int i=0; i<m_wInQueu; i++ ) { if ( m_pAryHdr[i] ) delete[] m_pAryHdr[i]; } memset ( m_pAryHdr, 0, sizeof(WAVEHDR*)*m_wInQueu ); delete[] m_pAryHdr; m_pAryHdr = NULL; } } void CAudioPlayRec::SetBkColor(COLORREF clr) { m_clrBK = clr; if ( m_brsBkGnd.GetSafeHandle() ) { m_brsBkGnd.DeleteTempMap(); m_brsBkGnd.DeleteObject(); } m_brsBkGnd.CreateSolidBrush ( clr ); if ( ::IsWindow ( m_hWnd ) ) Invalidate (); if ( m_PenB.GetSafeHandle() ) m_PenB.DeleteObject (); m_PenB.CreatePen ( PS_SOLID, 0, m_clrBK ); } // // 从立体声PCM数据中提取单声道数据,结果保存到 m_szLeftInData 、 m_szRightInData 中 // return : ------------------------------------------------------------------------------ // 单声道数据长度(字节) // int CAudioPlayRec::PickupMonoData ( WORD wBitsPerSample, char *szOrgData, int nOrgSize ) { if ( m_Format.nChannels == 1 ) { ASSERT ( m_dwQueuBufferSize >= (DWORD)nOrgSize ); memcpy ( m_szLeftInData, szOrgData, nOrgSize ); memcpy ( m_szRightInData, szOrgData, nOrgSize ); return nOrgSize; } ASSERT ( szOrgData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); ASSERT ( m_szLeftInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); ASSERT ( m_szRightInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); DWORD dwBytesPerSample = wBitsPerSample/8; int nDestBytes_Left = 0, nDestBytes_Right = 0; for ( int i=0; i<nOrgSize; i+=2*dwBytesPerSample ) { memcpy ( m_szLeftInData+nDestBytes_Left, szOrgData+i, dwBytesPerSample ); nDestBytes_Left += dwBytesPerSample; memcpy ( m_szRightInData+nDestBytes_Right, szOrgData+i+dwBytesPerSample, dwBytesPerSample ); nDestBytes_Right += dwBytesPerSample; } ASSERT ( nDestBytes_Left == nDestBytes_Right ); return nDestBytes_Left; } // // 为mp3编码装载动态库 // BOOL CAudioPlayRec::LoadMp3DllFunc () { if ( m_ForMp3_hDLL_LameEnc ) return TRUE; BE_VERSION Version ={0,}; // Load lame_enc.dll library (Make sure though that you set the // project/settings/debug Working Directory correctly, otherwhise the DLL can't be loaded m_ForMp3_hDLL_LameEnc = LoadLibrary ( "lame_enc.dll" ); if( NULL == m_ForMp3_hDLL_LameEnc ) { AfxMessageBox ( "Error loading lame_enc.DLL" ); return FALSE; } // Get Interface functions from the DLL m_ForMp3_Proc_hInitStream = (BEINITSTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEINITSTREAM); m_ForMp3_Proc_hEncodeChunk = (BEENCODECHUNK) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEENCODECHUNK); m_ForMp3_Proc_hDeinitStream = (BEDEINITSTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEDEINITSTREAM); m_ForMp3_Proc_hCloseStream = (BECLOSESTREAM) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BECLOSESTREAM); m_ForMp3_Proc_hVersion = (BEVERSION) GetProcAddress(m_ForMp3_hDLL_LameEnc, TEXT_BEVERSION); m_ForMp3_Proc_hWriteVBRHeader = (BEWRITEVBRHEADER) GetProcAddress(m_ForMp3_hDLL_LameEnc,TEXT_BEWRITEVBRHEADER); m_ForMp3_Proc_hWriteInfoTag = (BEWRITEINFOTAG) GetProcAddress(m_ForMp3_hDLL_LameEnc,TEXT_BEWRITEINFOTAG); // Check if all interfaces are present if ( !m_ForMp3_Proc_hInitStream || !m_ForMp3_Proc_hEncodeChunk || !m_ForMp3_Proc_hDeinitStream || !m_ForMp3_Proc_hCloseStream || !m_ForMp3_Proc_hVersion || !m_ForMp3_Proc_hWriteVBRHeader ) { AfxMessageBox ( "Unable to get LAME interfaces" ); return FALSE; } // Get the version number m_ForMp3_Proc_hVersion ( &Version ); TRACE ( "lame_enc.dll version %u.%02u (%u/%u/%u)\n" "lame_enc Engine %u.%02u\n" "lame_enc homepage at %s\n\n", Version.byDLLMajorVersion, Version.byDLLMinorVersion, Version.byDay, Version.byMonth, Version.wYear, Version.byMajorVersion, Version.byMinorVersion, Version.zHomepage ); return TRUE; } // // 为mp3编码做准备 // BOOL CAudioPlayRec::PrepareEncodeMp3 ( LPCTSTR lpszMp3FileName, ENUM_FILE_CHANNEL eFileChannel ) { ASSERT ( m_ForMp3_hDLL_LameEnc ); ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); if ( !m_ForMp3_hDLL_LameEnc ) return FALSE; if ( m_pFileMp3[eFileChannel] ) // 已经开始纪录mp3文件了 return TRUE; ASSERT ( lpszMp3FileName && strlen(lpszMp3FileName) > 0 ); m_csMp3FileName[eFileChannel] = lpszMp3FileName; BE_ERR err = 0; BE_CONFIG beConfig = {0}; memset ( &beConfig, 0, sizeof(beConfig) ); // clear all fields // use the LAME config structure beConfig.dwConfig = BE_CONFIG_LAME; WAVEFORMATEX wfx = m_Format; if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) wfx.nChannels = 1; // this are the default settings for testcase.wav int nMode = (wfx.nChannels==1)?BE_MP3_MODE_MONO:BE_MP3_MODE_JSTEREO; beConfig.format.LHV1.dwStructVersion = 1; beConfig.format.LHV1.dwStructSize = sizeof(beConfig); beConfig.format.LHV1.dwSampleRate = wfx.nSamplesPerSec; // INPUT FREQUENCY beConfig.format.LHV1.dwReSampleRate = 0; // DON"T RESAMPLE beConfig.format.LHV1.nMode = nMode; // OUTPUT IN STREO beConfig.format.LHV1.dwBitrate = 128; // MINIMUM BIT RATE beConfig.format.LHV1.nPreset = LQP_R3MIX; // QUALITY PRESET SETTING beConfig.format.LHV1.dwMpegVersion = MPEG1; // MPEG VERSION (I or II) beConfig.format.LHV1.dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL beConfig.format.LHV1.dwEmphasis = 0; // NO EMPHASIS TURNED ON beConfig.format.LHV1.bOriginal = TRUE; // SET ORIGINAL FLAG beConfig.format.LHV1.bWriteVBRHeader = TRUE; // Write INFO tag // beConfig.format.LHV1.dwMaxBitrate = 320; // MAXIMUM BIT RATE // beConfig.format.LHV1.bCRC = TRUE; // INSERT CRC // beConfig.format.LHV1.bCopyright = TRUE; // SET COPYRIGHT FLAG // beConfig.format.LHV1.bPrivate = TRUE; // SET PRIVATE FLAG // beConfig.format.LHV1.bWriteVBRHeader = TRUE; // YES, WRITE THE XING VBR HEADER // beConfig.format.LHV1.bEnableVBR = TRUE; // USE VBR // beConfig.format.LHV1.nVBRQuality = 5; // SET VBR QUALITY beConfig.format.LHV1.bNoRes = TRUE; // No Bit resorvoir // Preset Test // beConfig.format.LHV1.nPreset = LQP_PHONE; // Init the MP3 Stream DWORD dwMP3Buffer = 0; err = m_ForMp3_Proc_hInitStream ( &beConfig, &m_dwSamplesEncodeMp3Block, &dwMP3Buffer, &m_ForMp3_hStream[eFileChannel] ); m_ForMp3_dwWaveBufferSize = m_wInQueu*m_dwQueuBufferSize; // Check result if ( err != BE_ERR_SUCCESSFUL ) { AfxMessageBox ( "Error opening encoding stream (%lu)", err); return FALSE; } // Open MP3 file ::DeleteFile ( lpszMp3FileName ); m_pFileMp3[eFileChannel] = fopen ( lpszMp3FileName, "wb+" ); if ( m_pFileMp3[eFileChannel] == NULL ) { CString csMsg; csMsg.Format ( "Error creating file %s", lpszMp3FileName ); AfxMessageBox ( csMsg ); return FALSE; } // Allocate buffer if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete[] m_ForMp3_pMP3Buffer[eFileChannel]; m_ForMp3_pMP3Buffer[eFileChannel] = new BYTE[dwMP3Buffer]; if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete[] m_ForMp3_pWaveBuffer[eFileChannel]; m_ForMp3_pWaveBuffer[eFileChannel] = new BYTE[m_ForMp3_dwWaveBufferSize]; // Check if Buffer are allocated properly if( !m_ForMp3_pMP3Buffer[eFileChannel] || !m_ForMp3_pWaveBuffer[eFileChannel] ) { AfxMessageBox ( "Out of memory" ); return FALSE; } return TRUE; } BOOL CAudioPlayRec::WaveBufferMp3Encode ( char *szWavData, int nWavSize, ENUM_FILE_CHANNEL eFileChannel) { ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); DWORD dwWrite =0; BE_ERR err =0; // Encode samples err = m_ForMp3_Proc_hEncodeChunk ( m_ForMp3_hStream[eFileChannel], nWavSize/sizeof(SHORT), (SHORT*)szWavData, m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite ); // Check result if ( err != BE_ERR_SUCCESSFUL ) { m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] ); CString csMsg; csMsg.Format ( "m_ForMp3_Proc_hEncodeChunk() failed (%lu)", err ); AfxMessageBox ( csMsg ); return FALSE; } // write dwWrite bytes that are returned in tehe m_ForMp3_pMP3Buffer to disk if(fwrite(m_ForMp3_pMP3Buffer[eFileChannel],1,dwWrite,m_pFileMp3[eFileChannel]) != dwWrite) { AfxMessageBox ( "Output file write error" ); return FALSE; } return TRUE; } // // 结束mp3编码 // void CAudioPlayRec::EndEncodeMp3 ( ENUM_FILE_CHANNEL eFileChannel ) { if ( !m_pFileMp3[eFileChannel] ) return; ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); DWORD dwWrite =0; BE_ERR err =0; if ( !m_ForMp3_Proc_hDeinitStream ) return; // Deinit the stream err = m_ForMp3_Proc_hDeinitStream ( m_ForMp3_hStream[eFileChannel], m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite ); // Check result if ( err != BE_ERR_SUCCESSFUL ) { m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] ); CString csMsg; csMsg.Format ( "beExitStream failed (%lu)", err ); AfxMessageBox ( csMsg ); return; } // Are there any bytes returned from the DeInit call? // If so, write them to disk if ( dwWrite ) { if( fwrite ( m_ForMp3_pMP3Buffer[eFileChannel], 1, dwWrite, m_pFileMp3[eFileChannel] ) != dwWrite ) { AfxMessageBox ( "Output file write error" ); return; } } // close the MP3 Stream ASSERT ( m_ForMp3_Proc_hCloseStream ); m_ForMp3_Proc_hCloseStream( m_ForMp3_hStream[eFileChannel] ); // Delete Buffer if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete [] m_ForMp3_pMP3Buffer[eFileChannel]; m_ForMp3_pMP3Buffer[eFileChannel] = NULL; if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete [] m_ForMp3_pWaveBuffer[eFileChannel]; m_ForMp3_pWaveBuffer[eFileChannel] = NULL; // Close output file fclose( m_pFileMp3[eFileChannel] ); m_pFileMp3[eFileChannel] = NULL; if ( m_ForMp3_Proc_hWriteInfoTag ) { // Write the INFO Tag m_ForMp3_Proc_hWriteInfoTag ( m_ForMp3_hStream[eFileChannel], m_csMp3FileName[eFileChannel] ); } else { m_ForMp3_Proc_hWriteVBRHeader( m_csMp3FileName[eFileChannel] ); } } // // 释放mp3编码模块所申请的资源 // void CAudioPlayRec::FreeMp3Encode () { if ( m_ForMp3_hDLL_LameEnc ) ::FreeLibrary ( m_ForMp3_hDLL_LameEnc ); m_ForMp3_hDLL_LameEnc = NULL; ResetMp3EncodeVar (); } void CAudioPlayRec::ResetMp3EncodeVar() { for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel<ENUM_FILE_CHANNEL_NUM; eFileChannel++ ) { m_ForMp3_pMP3Buffer[eFileChannel] = NULL; m_ForMp3_pWaveBuffer[eFileChannel] = NULL; m_pFileMp3[eFileChannel] = NULL; m_csMp3FileName[eFileChannel] = ""; m_ForMp3_dwWaveDataBytes[eFileChannel] = 0; m_ForMp3_hStream[eFileChannel] = 0; } m_ForMp3_Proc_hInitStream = NULL; m_ForMp3_Proc_hEncodeChunk = NULL; m_ForMp3_Proc_hDeinitStream = NULL; m_ForMp3_Proc_hCloseStream = NULL; m_ForMp3_Proc_hVersion = NULL; m_ForMp3_Proc_hWriteVBRHeader = NULL; m_ForMp3_Proc_hWriteInfoTag = NULL; m_ForMp3_hDLL_LameEnc = NULL; m_ForMp3_dwWaveBufferSize = 0; m_dwSamplesEncodeMp3Block = 0; } BOOL CAudioPlayRec::PreTranslateMessage(MSG* pMsg) { if ( pMsg->message >= MM_WOM_OPEN && pMsg->message <= MM_MOM_DONE ) { switch ( pMsg->message ) { case MM_WIM_DATA: OnMM_WIM_DATA ( pMsg->wParam, pMsg->lParam ); break; case MM_WIM_CLOSE: m_eStatus = ENUM_STATUS_READY; m_nDataQueueNum = 0; m_bRecording = FALSE; break; case MM_WOM_DONE: OnMM_WOM_DONE ( pMsg->wParam, pMsg->lParam ); break; case MM_WOM_CLOSE: m_eStatus = ENUM_STATUS_READY; m_bRecording = FALSE; break; case MM_WIM_OPEN: break; case MM_WOM_OPEN: break; } if ( m_Proc_CallbackNotify ) { m_Proc_CallbackNotify ( pMsg->message, m_wParam ); } } return CWnd::PreTranslateMessage(pMsg); } void CAudioPlayRec::OnTimer(UINT nIDEvent) { switch ( nIDEvent ) { case TIMER_EVENT_STOPREC: KillTimer ( nIDEvent ); StopRec (); break; case TIMER_EVENT_STOPPLAY: KillTimer ( nIDEvent ); StopPlay (); break; } CWnd::OnTimer(nIDEvent); } void CAudioPlayRec::StopAndFreeAll () { if ( m_hRecord ) { StopRec (); } if ( m_hPlay ) { StopPlay (); } FreeBuffer (); } // // 获取系统中有多少个可以录音的声卡 // UINT CAudioPlayRec::GetWaveInCount() { return waveInGetNumDevs(); } // // 获取某录音声卡的名字 // CString CAudioPlayRec::GetWaveInName(UINT uDeviceID) { ASSERT ( uDeviceID < GetWaveInCount() ); WAVEINCAPS tagCaps; switch ( waveInGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps)) ) { case MMSYSERR_NOERROR: return tagCaps.szPname; break; default: return ""; } } // // 获取系统中有多少个可以放音的声卡 // UINT CAudioPlayRec::GetWaveOutCount() { return waveOutGetNumDevs(); } // // 获取某放音声卡的名字 // CString CAudioPlayRec::GetWaveOutName(UINT uDeviceID) { ASSERT ( uDeviceID < GetWaveOutCount() ); WAVEOUTCAPS tagCaps; switch (waveOutGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps))) { case MMSYSERR_NOERROR: return tagCaps.szPname; break; default: return ""; } } // // 设置录音设备的来源和音量 // void CAudioPlayRec::SetWaveInDevice ( BOOL bLineIn, DWORD dwVolume/*=ULONG_MAX*/ ) { CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID ); VolumeInXXX.SetCurrentVolume ( ( dwVolume == ULONG_MAX ) ? VolumeInXXX.GetMaximalVolume() : dwVolume ); VolumeInXXX.Enable (); } // // 获取录音设备音量 // DWORD CAudioPlayRec::GetWaveInVolume ( BOOL bLineIn, DWORD *pdwMaxVolume/*=NULL*/, DWORD *pdwMinVolume/*=NULL*/ ) { CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID ); if ( pdwMaxVolume ) *pdwMaxVolume = VolumeInXXX.GetMaximalVolume (); if ( pdwMinVolume ) *pdwMinVolume = VolumeInXXX.GetMinimalVolume (); return VolumeInXXX.GetCurrentVolume (); } // // 获取当前系统中 WAVE_MAPPER 在使用的声卡序号 // int CAudioPlayRec::GetAvailableDeviceIndex() { for ( DWORD i=0; i<GetWaveInCount(); i++ ) { CVolumeInXXX VolumeInXXX ( 0, i ); if ( VolumeInXXX.IsAvailable () ) return i; } return -1; }